package com.cube.share.base.templates;

import com.cube.share.base.function.SerializableFunction;
import com.cube.share.base.utils.ClassUtils;
import org.springframework.util.SerializationUtils;

import java.io.*;
import java.lang.invoke.MethodHandleInfo;
import java.util.Objects;

/**
 * @author poker.li
 * @date 2021/8/8 14:33
 * <p>
 * 将一个支持序列化的Function序列化为 SerializedLambda
 */
public class SerializedLambda implements Serializable {

    private static final long serialVersionUID = -5224537500283908722L;

    private Class<?> capturingClass;
    private String functionalInterfaceClass;
    private String functionalInterfaceMethodName;
    private String functionalInterfaceMethodSignature;
    private String implClass;
    private String implMethodName;
    private String implMethodSignature;
    private int implMethodKind;
    private String instantiatedMethodType;
    private Object[] capturedArgs;

    public static SerializedLambda resolve(SerializableFunction<?, ?> lambda) {
        if (!lambda.getClass().isSynthetic()) {
            throw new IllegalStateException("该方法仅能传入 lambda 表达式产生的合成类");
        }
        try (ObjectInputStream ojbIn = new ObjectInputStream(new ByteArrayInputStream(Objects.requireNonNull(SerializationUtils.serialize(lambda)))) {
            @Override
            protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
                Class<?> clazz;
                try {
                    clazz = ClassUtils.toClass(desc.getName());
                } catch (Exception e) {
                    clazz = super.resolveClass(desc);
                }
                return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz;
            }
        }) {
            return (SerializedLambda) ojbIn.readObject();
        } catch (ClassNotFoundException | IOException e) {
            throw new IllegalStateException("This is impossible to happen");
        }
    }

    public String getCapturingClass() {
        return capturingClass.getName().replace('.', '/');
    }

    public String getFunctionalInterfaceClass() {
        return functionalInterfaceClass;
    }

    public String getFunctionalInterfaceMethodName() {
        return functionalInterfaceMethodName;
    }

    /**
     * 获取接口 class
     *
     * @return 返回 class 名称
     */
    public String getFunctionalInterfaceClassName() {
        return normalizedName(functionalInterfaceClass);
    }

    public String getFunctionalInterfaceMethodSignature() {
        return functionalInterfaceMethodSignature;
    }

    public String getImplMethodSignature() {
        return implMethodSignature;
    }

    public int getImplMethodKind() {
        return implMethodKind;
    }

    public final String getInstantiatedMethodType() {
        return instantiatedMethodType;
    }

    public int getCapturedArgCount() {
        return capturedArgs.length;
    }

    public Object getCapturedArg(int i) {
        return capturedArgs[i];
    }

    /**
     * 获取 class 的名称
     *
     * @return 类名
     */
    public String getImplClassName() {
        return normalizedName(implClass);
    }

    /**
     * 获取实现者的方法名称
     *
     * @return 方法名称
     */
    public String getImplMethodName() {
        return implMethodName;
    }

    /**
     * 正常化类名称,将类名称中的 / 替换为 .
     *
     * @param name 类名称
     * @return 正常的类名
     */
    private String normalizedName(String name) {
        return name.replace("/", ".");
    }

    @Override
    public String toString() {
        String implKind= MethodHandleInfo.referenceKindToString(implMethodKind);
        return String.format("SerializedLambda[%s=%s, %s=%s.%s:%s, " +
                        "%s=%s %s.%s:%s, %s=%s, %s=%d]",
                "capturingClass", capturingClass,
                "functionalInterfaceMethod", functionalInterfaceClass,
                functionalInterfaceMethodName,
                functionalInterfaceMethodSignature,
                "implementation",
                implKind,
                implClass, implMethodName, implMethodSignature,
                "instantiatedMethodType", instantiatedMethodType,
                "numCaptured", capturedArgs.length);
    }
}
